#ifndef _APPS_BWM_CCLIENT_H_
#define _APPS_BWM_CCLIENT_H_

#include "auto.h"
#include <buola/gl/ctexture.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11picture.h>
#include <buola/gui/x11/icccm.h>

#include "sshaperecovery.h"
#include "croot.h"
#include "cdecoration.h"

#include <buola/gui/x11/cx11dispatcher.h>
#include <buola/gui/x11/cx11sync.h>
#include <buola/gui/cwindow.h>

namespace buola { namespace bwm {

/////////////////////////////////////////////////////////////////////////////////

/// represents a client window


/// it doesn't matter if it is decorated, managed or non-managed, it will always
/// have a CClient representation
/// it is the class which controls all others
/////////////////////////////////////////////////////////////////////////////////

class CClient : public UUncopiable
{
public:
    //!client flags
    enum EFlags
    {
        FLAG_NONE=0,
        FLAG_MAPPED=(1<<1),
        FLAG_MANAGED=(1<<2),
        FLAG_COMPOSED=(1<<3),
        FLAG_STICKY=(1<<5),
        FLAG_ABOVE=(1<<6),
        FLAG_BELOW=(1<<7),
        FLAG_IS_BUOLA_DOCK=(1<<8),
        FLAG_DAMAGED=(1<<9),
        FLAG_WAITINGRESIZEALARM=(1<<10)
    };

    DECLARE_FRIEND_ENUM_BITOPS(EFlags)

    enum class EState
    {
        NONE=0,
        MAXHORZ=(1<<0),
        MAXVERT=(1<<1),
        MAXIMIZED=MAXHORZ|MAXVERT,
        FULLSCREEN=(1<<2),
        ICONIC=(1<<3),
        SHADED=(1<<4)
    };
    
    DECLARE_FRIEND_ENUM_CLASS_BITOPS(EState)

protected:
    enum ECompositeMode
    {
        COMPOSITE_SOLID,
        COMPOSITE_TRANS,
        COMPOSITE_ARGB
    };

    enum EAnimation
    {
        ANIM_NONE,
        ANIM_ACTIVATE,
        ANIM_FADEIN,
        ANIM_FADEOUT,
        ANIM_FADECHANGE,
        ANIM_ICONIFY,
        ANIM_DEICONIFY,
        ANIM_DRAGSTART,
        ANIM_DRAGMOVE,
        ANIM_DRAGRESIZE
    };
    
public:
//! \name constructor and destructorcreation
//@{
    CClient(Window pID,const XWindowAttributes &pXAttrs);
    virtual ~CClient();
//@}

protected:
//! \name internal initialization
//@{
    void StartManaging();
    void StopManaging();
    void StartComposer();
//@}

public:
//! \name management of other classes
//@{
    void DestroyDecoration();
//@}

//! \name data access helpers
//@{
    Window GetID()          {   return mID;                                 }
    gui::EWindowType GetWindowType()          {   return mWindowType;                                 }
    EState GetState()                   {   return mState;                  }
    double GetOpacity()          {   return mOpacity;                }
    double GetEffOpacity()       {   return mEffOpacity;            }
//@}

//! \name size & position
//@{
    const CPoint_i &GetXPos()                    {   return mXPos;           }
    const CSize_i &GetPhysicalSize()             {   return mPhysicalSize;   }
    const CPoint_i &GetPhysicalPos()             {   return mPhysicalPos;    }
//@}

//////////////////////////////
// client window operations
//
    void MoveClient(const CPoint_i &pNewPos);
    void MoveResizeClient(const CPoint_i &pNewPos,const CSize_i &pNewSize);
    void ResizeClient(const CSize_i &pNewSize);
    void ShowClient(bool);

///////////////////////////////
// window manager operations
//
//State&Visibility
public:
    void SetOpacity(double pOpacity);

protected:
////////////////////////////////
// property general functions
//
    void ReadCommonProperties();

public:
///////////////////////////////
// property event processing
//
public:
    bool IsOnDesktop(int i) {   return mOnDesktop[i];                   }
    bool IsOnAllDesktops()      {   return (mDesktop==0xffffffff||
                                                GetFlag(FLAG_STICKY)||
                                        mWindowType==gui::EWindowType::DESKTOP);      }

    bool IsTransient()          {   return (mTransientFor!=None);      }
    Window  GetTransientFor()   {   return mTransientFor;                }

    void WMSendMessage(Atom, long);

protected:
//////////////////////////////
// internal flags functions
//
    void SetFlags(EFlags pFlags)    {   mFlags=pFlags;                      }
    EFlags GetFlags()               {   return mFlags;                      }

    void SetFlag(EFlags pFlag)      {   mFlags|=pFlag;                      }
    void UnsetFlag(EFlags pFlag)    {   mFlags&=(~pFlag);                   }

public:
    bool GetFlag(EFlags pFlag)      {   return ((mFlags&pFlag)==pFlag); }

protected:
///////////////////////////
// internal calculations
//
    void CalcVirtualViewport();
    void CalcShape();
    void CalcDesktops();
public:

/////////////////////////
// painting and damage
//
public:
    void DamageRegion(const CRegion &pRegion)
    {
        for(int i=0;i<gRoot->GetDesktopCount();i++)
        {
            if(!IsOnDesktop(i)||!gRoot->IsDesktopVisible(i)) continue;
            gRoot->DamageDesktop(pRegion-mRegions.mObscured[i],i);
        }
    }

    void DamageClient()
    {
        for(int i=0;i<gRoot->GetDesktopCount();i++)
        {
            if(!IsOnDesktop(i)||!gRoot->IsDesktopVisible(i)) continue;
            gRoot->DamageDesktop(mRegions.mClient[i]-
                                 mRegions.mObscured[i],i);
        }
    }

    void DamageRect(const CRect_i &pRect)
    {
        for(int i=0;i<gRoot->GetDesktopCount();i++)
        {
            if(!IsOnDesktop(i)||!gRoot->IsDesktopVisible(i)) continue;
            CRegion lRegion(pRect+mPhysicalPos+mVirtualViewportOffset[i]);
            gRoot->DamageDesktop(lRegion-mRegions.mObscured[i],i);
        }
    }

    void DamageWhole()
    {
        for(int i=0;i<gRoot->GetDesktopCount();i++)
        {
            if(!IsOnDesktop(i)||!gRoot->IsDesktopVisible(i)) continue;
            gRoot->DamageDesktop(mRegions.mWhole[i]-
                                 mRegions.mObscured[i],i);
        }
    }

    void DamageDecoration()
    {
        for(int i=0;i<gRoot->GetDesktopCount();i++)
        {
            if(!IsOnDesktop(i)||!gRoot->IsDesktopVisible(i)) continue;
            gRoot->DamageDesktop(mRegions.mWhole[i]-
                                 mRegions.mClient[i]-
                                 mRegions.mObscured[i],i);
        }
    }

    CRect_i GetWholeRect(int pDesktop)    {   return mRegions.mWhole[pDesktop].BoundingBox();        }
    CRect_i GetWholeRect()    {   return GetWholeRect(
                                                gRoot->GetActiveDesktop()); }

    void DrawGeometry(const CRegion&,int pDesktop);
    void DrawTexture(EDrawMode,const CRegion&,int pDesktop);
    void DrawTextureRect(EDrawMode,const CRect_d &pRect,int pDesktop,bool pMipmap=false);

public:
    void Clip(SGLCompositeContext &pC);
    void Paint(SGLCompositeContext &pC);
    void PaintRect(const CRect_d &pRect);
    void PaintBaseLine(int pDesktop,bool pMipmap=false);
    void PaintThumbnail();

///////////////
// animation
//
    void StepFading(const CMonotonicTime &pTime);
    void StartFading(double pStart,double pFinish,const chrono::microseconds &pVMax);

    void StartAnimation(EAnimation pAnim,const CRect_d &pRect=CRect_d());
    void StartAnimationDrag(const CPoint_d &pPos,gui::EDragState pDrag);
    void StepAnimationDrag(const CPoint_d &pPos,const CSize_d &pSize);
    void FinishAnimation();

///////////////////////
// composite manager
//
    void CreatePixmap();
    void DiscardPixmap();
    bool CreateTexture();
    void EnableTexture(bool pFastFilter,bool pMipMaps=false);
    void DisableTexture();
    void DetermineMode();
    void DetermineMatrix();
    void DetermineRegions();

////////////////////
// event handlers
//
    void OnLoop(const CMonotonicTime &pTime);

    virtual void OnConfigureNotify(XConfigureEvent*) {}
    virtual void OnMapNotify(bool pWMStarting) {}
    virtual void OnUnmapNotify(bool pSynthetic) {}
    virtual void OnDestroyNotify() {}
    void OnClientReparented();
    void OnShapeChanged();
    void OnDesktopSwitched();
    void OnDesktopsChanged();
    void OnGeometryChanged(bool pSize);
    void OnDamage(const CRect_i &pRect);

//////////////////////
// static functions
//
    static bool CompareByStack(CClient *p1,CClient *p2);

////////////////////////////
/////////// data ///////////
////////////////////////////

    void *GetRendererData(int pSlot);
    void SetRendererData(int pSlot,void *pData);

/////////////////////////////
// root window and members
//
protected:
    bool mOnDesktop[MAX_DESKTOPS];      //true if the window is (even
                                        //partially) in that desktop

public:
    CDecoration *mDecoration;

    Window mID;

    EZPriority mZPriority;
    gui::EWindowType mWindowType;
    Window mTransientFor;
    unsigned int mDesktop;
    
protected:
///////////////////////
// size and position
//
    int mDepth;
    Visual *mVisual;
    int mBorderWidth;
    CPoint_i mXPos;           //current pos of X client (includes virtual offset)
    CSize_i mPhysicalSize;    //current size of client (includes border)
    CPoint_i mPhysicalPos;    //current pos of client (includes border but
                            //not virtual offset)

    CPoint_i mVirtualViewportOffset[MAX_DESKTOPS];
                            //offset in physical position for every desktop
                            //VirtualViewport[mDesktop]-
                            //VirtualViewport[i]

protected:
///////////////////////////////
// window manager attributes
//
    EState mState;
    EFlags mFlags;

    double mOpacity;


public:
////////////////////
// stacking order
//
    int mStackingOrder;

////////////////////
// animation data
//
    EAnimation mAnimating;
    double mEffOpacity;

/////////////
// friends
//
    friend class CEventManager;

/////////////
// regions
//
    struct
    {
        CRegion mClient[MAX_DESKTOPS];  //region occupied by the client,
                                            //including border
        CRegion mWhole[MAX_DESKTOPS];   //region occupied by the client
                                            //and the frame
        CRegion mObscured[MAX_DESKTOPS];    //region obscured by opaque things
                                            //above
        CRegion mRemaining;             //region which has to be painted
                                            //in the transparent phase
        CRegion mShape;
    } mRegions;

//////////////////////////////////
// graphic data for compositing
//
    XRenderPictFormat *mPictFormat;
    struct
    {
        gl::CTexture mTexture;
        GLXPixmap mGLXPixmap;
        bool mBound;
        mat::CMat23d mMatrix;
        GLenum mFilter;
    } mTexture;
    Pixmap mPixmap;
    Damage mXDamage;
    ECompositeMode mCompositeMode;
    mat::CMat23d mMatrix[MAX_DESKTOPS];
    SShapeRecovery *mFadeInfo;
    CClient *mPrevTrans;
    bool mShaped;

    std::vector<void*> mRendererData;
};

/*namespace bwm*/ } /*namespace buola*/ }

#endif
